package com.net.yl;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.net.RetrofitUtils;
import com.net.yl.entity.ActivateEntity;
import com.net.yl.entity.CreatePayResponseEntity;
import com.net.yl.entity.PayOrderEntity;
import com.net.yl.entity.PayResultEntity;
import com.net.yl.entity.ResponseEntity;
import com.net.yl.entity.SyncDataProductEntity;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Type;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;

/**
 * create by cjx on 2024/7/15
 * desc:
 */
public class ApiRepository {
    private final String CODE_SUCCESS = "0000";

    private static ApiRepository repository;
    private String number;
    private String pwd;
    private Api api;

    public static ApiRepository getInstance() {
        if (repository == null) {
            synchronized (ApiRepository.class) {
                if (repository == null) {
                    repository = new ApiRepository();
                }
            }
        }
        return repository;
    }

    private ApiRepository() {
    }

    public ApiRepository init(String url) {
        this.api = RetrofitUtils.getInstance().create(url, Api.class);
        return this;
    }

    public void bindDevice(String number, String pwd) {
        this.number = number;
        this.pwd = pwd;
    }

    // 激活设备
    public Disposable activate(String sn, Consumer<ResponseEntity<ActivateEntity>> onNext,
                               Consumer<Throwable> onError) {
        HashMap<String, Object> params = new HashMap<>();
        params.put("sn", sn);
        String body = new Gson().toJson(params);
        Observable<ResponseBody> observable = api.activate(getRequestBody(body));
        return observable.subscribeOn(Schedulers.io())
                .map((Function<ResponseBody, ResponseEntity<ActivateEntity>>) responseBody ->
                        parserBody(responseBody, new TypeToken<ActivateEntity>() {
                        }.getType()))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(onNext, onError);

    }

    // 获取产品信息
    public Disposable syncProduct(Consumer<ResponseEntity<SyncDataProductEntity>> onNext, Consumer<Throwable> onError) {
        Observable<ResponseBody> observable = api.syncProduct(initHeader(null));
        return observable.subscribeOn(Schedulers.io())
                .map((Function<ResponseBody, ResponseEntity<SyncDataProductEntity>>) responseBody ->
                        parserBody(responseBody, new TypeToken<SyncDataProductEntity>() {
                        }.getType()))
                .observeOn(Schedulers.io()).subscribe(onNext, onError);
    }

    public Disposable uploadCrashLog(File file) {
        initHeader(null);

        MediaType parse = MediaType.parse("multipart/form-data");
        if (parse == null) {
            return null;
        }

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);
        RequestBody fileBody = RequestBody.create(parse, file);
        RequestBody body = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("dateTime", sdf.format(new Date()))
                .addFormDataPart("file", file.getName(), fileBody)
                .build();
        Observable<ResponseBody> observable = api.logCrashInsert(body);
        return observable.subscribeOn(Schedulers.io())
                .map((Function<ResponseBody, ResponseEntity<Boolean>>) responseBody ->
                        parserBody(responseBody, Boolean.class))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(result -> {

                }, throwable -> {

                });
    }

    // 创建订单生成收款码
    public Disposable createPayCode(PayOrderEntity order, Consumer<ResponseEntity<CreatePayResponseEntity>> onNext, Consumer<Throwable> onError) {
        HashMap<String, String> params = new HashMap<>();
        params.put("orderNo", order.orderNo);
        params.put("totalAmount", order.totalAmount);
        params.put("goods", new Gson().toJson(order.goods));
        String body = new Gson().toJson(order);
        Observable<ResponseBody> observable = api.createPayCode(initHeader(params), getRequestBody(body));
        return observable.subscribeOn(Schedulers.io())
                .map((Function<ResponseBody, ResponseEntity<CreatePayResponseEntity>>) responseBody ->
                        parserBody(responseBody, new TypeToken<CreatePayResponseEntity>() {
                        }.getType()))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(onNext, onError);
    }

    // 创建订单生成收款码
    public Disposable payWithCode(PayOrderEntity order, Consumer<ResponseEntity<Boolean>> onNext, Consumer<Throwable> onError) {
        HashMap<String, String> params = new HashMap<>();
        params.put("orderNo", order.orderNo);
        params.put("totalAmount", order.totalAmount);
        params.put("goods", new Gson().toJson(order.goods));
        params.put("payCode", order.payCode);
        String body = new Gson().toJson(order);
        Observable<ResponseBody> observable = api.payWithCode(initHeader(params), getRequestBody(body));
        return observable.subscribeOn(Schedulers.io())
                .map((Function<ResponseBody, ResponseEntity<Boolean>>) responseBody ->
                        parserBody(responseBody, new TypeToken<Boolean>() {
                        }.getType()))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(onNext, onError);
    }

    public Disposable queryPayStatus(String orderNo, Consumer<ResponseEntity<PayResultEntity>> onNext, Consumer<Throwable> onError) {
        HashMap<String, String> params = new HashMap<>();
        params.put("orderNo", orderNo);
        String body = new Gson().toJson(params);
        Observable<ResponseBody> observable = api.queryPayStatus(initHeader(params), getRequestBody(body));
        return observable.subscribeOn(Schedulers.io())
                .map((Function<ResponseBody, ResponseEntity<PayResultEntity>>) responseBody ->
                        parserBody(responseBody, new TypeToken<PayResultEntity>() {
                        }.getType()))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(onNext, onError);
    }

    public Disposable closePay(String orderNo, Consumer<ResponseEntity<Boolean>> onNext, Consumer<Throwable> onError) {
        HashMap<String, String> params = new HashMap<>();
        params.put("orderNo", orderNo);
        String body = new Gson().toJson(params);
        Observable<ResponseBody> observable = api.closePay(initHeader(params), getRequestBody(body));
        return observable.subscribeOn(Schedulers.io())
                .map((Function<ResponseBody, ResponseEntity<Boolean>>) responseBody ->
                        parserBody(responseBody, new TypeToken<PayResultEntity>() {
                        }.getType()))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(onNext, onError);
    }

    private RequestBody getRequestBody(String body) {
        return RequestBody.create(MediaType.parse("Content-Type:application/json"), body);
    }


    /**
     * 内部服务器通用转换工具， 如果你的项目用的不是这套模型，自己差异包里面搞
     */
    private <T> ResponseEntity<T> parserBody(ResponseBody responseBody, Type type) {
        ResponseEntity<T> rb = null;
        try {
            if (responseBody != null) {
                String body = responseBody.string();
                if (body.length() == 0) {
                    return null;
                }
                try {
                    JSONObject jsonObject = new JSONObject(body);
                    rb = new ResponseEntity<>();
                    if (jsonObject.has("code")) {
                        String code = jsonObject.getString("code");
                        rb.success = CODE_SUCCESS.equals(code);
                        rb.extra = code;
                        if (rb.success) {
                            if (type != null && jsonObject.has("data")) {
                                rb.data = new Gson().fromJson(jsonObject.getString("data"), type);
                            }
                        }
                    }
                    if (jsonObject.has("msg")) {
                        rb.message = jsonObject.getString("msg");
                    } else {
                        rb.message = "数据结构异常";
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return rb;
    }

    private TreeMap<String, String> initHeader(HashMap<String, String> param) {
        TreeMap<String, String> headers = new TreeMap<>();
        String timeStamp = String.valueOf(System.currentTimeMillis());
        headers.put("ts", timeStamp);
        headers.put("nonce", timeStamp);
        headers.put("sn", number);
        headers.put("version", "1");
        TreeMap<String, String> singMap = new TreeMap<>(headers);
        if (param != null && !param.isEmpty()) {
            singMap.putAll(param);
        }
        singMap.put("pwd", pwd);

        StringBuilder builder = new StringBuilder();
        for (Map.Entry<String, String> entry : singMap.entrySet()) {
            builder.append("&");
            builder.append(entry.getKey()).append("=").append(entry.getValue());
        }
        String toSignStr = builder.substring(1);
        try {
            MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
            byte[] bytes = sha256.digest(toSignStr.getBytes());
            StringBuilder hexSb = new StringBuilder(bytes.length * 2);
            for (byte b : bytes) {
                hexSb.append(String.format("%02x", b & 0xFF));
            }
            String signStr = hexSb.toString().toUpperCase();
            headers.put("sign", signStr);
        } catch (NoSuchAlgorithmException ignore) {

        }
        headers.remove("pwd");
        return headers;
    }
}
